// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <algorithm>
#include <type_traits>
#include <utility>

namespace deneb {

    /// A entry of references to the key and value of an entry in a cache.
    ///
    /// Instances of this class are usually the result of dereferencing an iterator.
    ///
    /// \tparam Key The key type of the pair.
    /// \tparam Value The value type of the pair.
    template<typename Key, typename Value>
    struct Entry final {
        using KeyType = Key;
        using ValueType = Value;
        using first_type = Key;
        using second_type = Value;

        /// Constructor.
        ///
        /// \param key The key of the entry.
        /// \param value The value of the entry.
        Entry(const Key &key, Value &value) : first(key), second(value) {
        }

        /// Generalized copy constructor.
        ///
        /// Mainly for conversion from non-const values to const values.
        ///
        /// \param other The entry to construct from.
        template<typename AnyKey,
                typename AnyValue,
                typename =
                std::enable_if_t<(std::is_convertible<AnyKey, Key>::value &&
                                  std::is_convertible<AnyValue, Value>::value)>>
        Entry(const Entry<AnyKey, AnyValue> &other)
                : first(other.first), second(other.second) {
        }

        /// Compares two entrys for equality.
        ///
        /// \param first The first entry to compare.
        /// \param second The second entry to compare.
        /// \returns True if the firest entry equals the second, else false.
        template<typename Pair, typename = typename Pair::first_type>
        friend bool operator==(const Entry &first, const Pair &second) noexcept {
            return first.first == second.first && first.second == second.second;
        }

        /// Compares two entrys for equality.
        ///
        /// \param first The first entry to compare.
        /// \param second The second entry to compare.
        /// \returns True if the first entry equals the second, else false.
        template<typename Pair, typename = typename Pair::first_type>
        friend bool operator==(const Pair &first, const Entry &second) noexcept {
            return second == first;
        }

        /// Compares two entrys for inequality.
        ///
        /// \param first The first entry to compare.
        /// \param second The second entry to compare.
        /// \returns True if the first entry does not equal the second, else false.
        template<typename Pair, typename = typename Pair::first_type>
        friend bool operator!=(const Entry &first, const Pair &second) noexcept {
            return !(first == second);
        }

        /// Compares two entrys for inequality.
        ///
        /// \param first The first entry to compare.
        /// \param second The second entry to compare.fdas
        /// \returns True if the first entry does not equal the second, else false.
        template<typename Pair, typename = typename Pair::first_type>
        friend bool operator!=(const Pair &first, const Entry &second) noexcept {
            return second != first;
        }

        /// \returns A `std::pair` instance with the key and value of this entry.
        operator std::pair<const Key &, Value &>() noexcept {
            return {first, second};
        }

        /// \returns The key of the entry (`first`).
        const Key &key() const noexcept {
            return first;
        }

        /// \returns The value of the entry (`second`).
        Value &value() noexcept {
            return second;
        }

        /// \returns The value of the entry (`second`).
        const Value &value() const noexcept {
            return second;
        }

        /// The key of the entry.
        const Key &first;

        /// The value of the entry.
        Value &second;
    };
}  // namespace deneb

